import { DeviceTool } from '../lib/deviceTool';
import { IvcsError } from '../base/error';
import { ivcsErrorCodeEnum } from '../base/const';
import { createCameraMicStream } from '../lib/irtcScript';
import Logger from './logger';
import { isNullObject } from '../lib/tool';
class Navigator extends DeviceTool {
	constructor() {
		super();
		// 发起视频呼叫之后，IOS 默认前置
		this.facingMode = 'user';
		// 设备默认索引
		this.deviceIndex = 0;
		this.userPhone = this.getUserPhone();
	}
	/**
	 * 获取设备
	 * @param {object} constraints 一个MediaStreamConstraints对象，指定要请求的媒体类型以及每种类型的任何要求
	 * @param {function} onSuccess 成功
	 * @param {function} onError  失败
	 */
	getUserMedia(constraints, onSuccess, onError) {
		if (window.navigator.mediaDevices.getUserMedia) {
			//最新标准API
			window.navigator.mediaDevices
				.getUserMedia(constraints)
				.then(stream => onSuccess(stream))
				.catch(e => onError(e));
		} else if (navigator.webkitGetUserMedia) {
			//webkit内核浏览器
			window.navigator
				.webkitGetUserMedia(constraints)
				.then(stream => onSuccess(stream))
				.catch(e => onError(e));
		} else if (navigator.mozGetUserMedia) {
			//Firefox浏览器
			window.navigator
				.mozGetUserMedia(constraints)
				.then(stream => onSuccess(stream))
				.catch(e => onError(e));
		} else if (navigator.getUserMedia) {
			//旧版API
			window.navigator
				.getUserMedia(constraints)
				.then(stream => onSuccess(stream))
				.catch(e => onError(e));
		}
	}

	/**
	 * 麦克风检测
	 * @param {function} onSuccess 成功
	 * @param {function} onError  失败，具体错误原因可参考https://developer.mozilla.org/en-US/docs/Web/API/MediaDevices/getUserMedia
	 */
	audioTest(onSuccess, onError) {
		let constraints = {
			audio: true,
			video: false,
		};
		this.getUserMedia(
			constraints,
			stream => {
				let audioTracks = stream.getAudioTracks();
				for (let track of audioTracks) {
					track.stop();
				}
				onSuccess('ok');
			},
			err => {
				let error = new IvcsError('70007', `audio test error ${err}`);
				onError(error);
			}
		);
	}

	/**
	 * 摄像头检测
	 * @param {string} myPreferredCameraDeviceId 摄像头的设备id
	 * @param {function} onSuccess 成功
	 * @param {function} onError  失败，具体错误原因可参考https://developer.mozilla.org/en-US/docs/Web/API/MediaDevices/getUserMedia
	 */
	videoTest(myPreferredCameraDeviceId, onSuccess, onError) {
		let videoConstraints = myPreferredCameraDeviceId
			? {
					deviceId: myPreferredCameraDeviceId,
			  }
			: true;
		let constraints = {
			audio: false,
			video: videoConstraints,
		};
		this.getUserMedia(
			constraints,
			stream => {
				let tracks = stream.getVideoTracks();
				for (let track of tracks) {
					track.stop();
				}
				onSuccess('ok');
			},
			err => {
				let error = new IvcsError('70006', `video test error ${err}`);
				onError(error);
			}
		);
	}

	/**
	 * 根据设备ID切换摄像头
	 * @param {string} cameraDevicesId 摄像头的设备id
	 * @param { Boolean } videoOnly 是否只切换成视频流，默认为false
	 * @param {function} onSuccess 成功
	 * @param {function} onError  失败，具体错误原因可参考https://developer.mozilla.org/en-US/docs/Web/API/MediaDevices/getUserMedia
	 */
	changeCameraWithDeviceId(cameraDevicesId, videoOnly = false) {
		return new Promise((resolve, reject) => {
			try {
				let videoConstraints = cameraDevicesId
					? {
							deviceId: cameraDevicesId,
					  }
					: true;
				let constraints = {
					audio: !videoOnly
						? {
								echoCancellation: true,
						  }
						: false,
					video: videoConstraints,
				};
				this.getUserMedia(
					constraints,
					stream => {
						resolve(stream);
					},
					err => {
						let exceptionMsg = err.name || err.message || err;
						let errorCode = ivcsErrorCodeEnum.ERROR_ZMS_CREATESTREAM_FAILED;
						if (exceptionMsg.toLowerCase().indexOf('aborterror') != -1) {
							errorCode = ivcsErrorCodeEnum.ERROR_ZMS_ABORTERROR;
						} else if (exceptionMsg.toLowerCase().indexOf('notallowederror') != -1) {
							errorCode = ivcsErrorCodeEnum.ERROR_ZMS_NOTALLOWEDERROR;
						} else if (exceptionMsg.toLowerCase().indexOf('notfounderror') != -1) {
							errorCode = ivcsErrorCodeEnum.ERROR_ZMS_NOTFOUNDERROR;
						} else if (exceptionMsg.toLowerCase().indexOf('notreadableerror') != -1) {
							errorCode = ivcsErrorCodeEnum.ERROR_ZMS_NOTREADABLEERROR;
						} else if (exceptionMsg.toLowerCase().indexOf('overconstrainederror') != -1) {
							errorCode = ivcsErrorCodeEnum.ERROR_ZMS_OVERCONSTRAINEDERROR;
						} else if (exceptionMsg.toLowerCase().indexOf('securityerror') != -1) {
							errorCode = ivcsErrorCodeEnum.ERROR_ZMS_SECURITYERROR;
						} else if (exceptionMsg.toLowerCase().indexOf('typeerror') != -1) {
							errorCode = ivcsErrorCodeEnum.ERROR_ZMS_TYPEERROR;
						}
						let error = new IvcsError(errorCode, `getUserMedia with deviceId ${cameraDevicesId} error ${err}`);
						reject(error);
					}
				);
			} catch (err) {
				let error = new IvcsError('70000', `getUserMedia error: with deviceId ${cameraDevicesId} error ${err}`);
				reject(error);
			}
		});
	}

	/**
	 * 根据设备ID切换麦克风
	 * @param {string} micDevicesId 麦克风的设备id
	 * @param {function} onSuccess 成功
	 * @param {function} onError  失败，具体错误原因可参考https://developer.mozilla.org/en-US/docs/Web/API/MediaDevices/getUserMedia
	 */
	changeMicWithDeviceId(micDevicesId) {
		return new Promise((resolve, reject) => {
			try {
				let constraints = {
					audio: {
						deviceId: micDevicesId,
						echoCancellation: true,
					},
					video: true,
				};
				this.getUserMedia(
					constraints,
					stream => {
						resolve(stream);
					},
					err => {
						let error = new IvcsError('70005', `change mic with deviceId error ${err}`);
						reject(error);
					}
				);
			} catch (err) {
				let error = new IvcsError('70005', `change camera mic deviceId error ${err}`);
				reject(error);
			}
		});
	}

	/**
	 * 根据参数更改本地视频流设置
	 * @param { MediaStream } localStream 本地创建的流
	 * @param { object } constraints 应用参数， constraints: {width: 1280,  height: 720, }; https://developer.mozilla.org/en-US/docs/Web/API/MediaStreamTrack/applyConstraints
	 * @returns 返回应用后的流
	 */
	async changeConstraints(localStream, videoParams) {
		return new Promise(async (resolve, reject) => {
			let track = localStream.mediaStream ? localStream.mediaStream.getVideoTracks()[0] : localStream.getVideoTracks()[0];
			if (track) {
				const { width, height } = track.getCapabilities();
				if (videoParams.width > width.max || videoParams.height > height.max) {
					let error = new IvcsError('70009', `stream apply constraints failed: constraints width or height max ${width.max}-${height.max}`);
					reject(error);
				} else {
					track
						.applyConstraints({ width: videoParams.width, height: videoParams.height })
						.then(() => {
							resolve(localStream.mediaStream);
						})
						.catch(e => {
							console.log(e);
						});
				}
			} else {
				let error = new IvcsError('70009', `stream track is null`);
				reject(error);
			}
		});
	}

	/**
	 * 苹果换摄像头流
	 * @param {string} facingMode 苹果摄像头前后标识
	 * @param {function} onSuccess 成功
	 * @param {function} onError  失败，具体错误原因可参考https://developer.mozilla.org/en-US/docs/Web/API/MediaDevices/getUserMedia
	 */
	changeIosCamera(facingMode, onSuccess, onError) {
		let constraints = {
			audio: {
				echoCancellation: true,
			},
			video: {
				facingMode: facingMode,
			},
		};
		this.getUserMedia(
			constraints,
			stream => {
				onSuccess(stream);
			},
			err => {
				console.log('changeIosCamera error', err);
				let error = new IvcsError('70004', `iOS device change camera error ${err}, faceMode:${facingMode},constraints:${constraints}`);
				onError(error);
			}
		);
	}

	/**
	 * 安卓和PC处理摄像头切换
	 * @param { Function } onSuccess
	 * @param { Function } onError {70001:mediaDevices|| enumerateDevices不支持;70002:没找到可用设备；70003:enumerateDevices报错；70008:创建的流为空}
	 * @param { Boolean } videoOnly 是否只切换成视频流，默认为false
	 * @returns
	 */
	getUserDevices(onSuccess, onError, videoOnly = false) {
		let userCameraDevices = [];
		let userAudioDevices = [];
		if (!navigator.mediaDevices || !navigator.mediaDevices.enumerateDevices) {
			let error = new IvcsError('70001', 'getUserDevices devices not support');
			onError(error);
			return;
		}
		try {
			navigator.mediaDevices
				.enumerateDevices()
				.then(function (devices) {
					devices.forEach(function (device) {
						if (device.kind === 'videoinput') {
							userCameraDevices.push({
								kind: device.kind,
								label: device.label,
								groupId: device.groupId,
								deviceId: device.deviceId,
							});
						} else if (device.kind === 'audioinput') {
							userAudioDevices.push({
								kind: device.kind,
								label: device.label,
								groupId: device.groupId,
								deviceId: device.deviceId,
							});
						}
					});
				})
				.then(async () => {
					let device = this.filterAndroidCameraLabel(userCameraDevices);
					if (device.length < 0) {
						let error = new IvcsError('70002', 'getUserDevices notfound devices');
						onError(error);
					} else if (this.userPhone === 'Webview' || this.userPhone === 'Android') {
						this.deviceIndex === 0 ? (this.deviceIndex = device.length - 1) : (this.deviceIndex = 0);
					} else {
						this.deviceIndex = this.deviceIndex + 1;
						if (this.deviceIndex >= device.length) {
							this.deviceIndex = 0;
						}
					}
					let deviceId = device[this.deviceIndex].deviceId;
					let stream = await this.changeCameraWithDeviceId(deviceId, videoOnly);
					if (stream) {
						onSuccess(stream);
					} else {
						let error = new IvcsError('70008', `getUserDevices change camera witch deviceId:${deviceId} error, stream is null`);
						onError(error);
					}
				})
				.catch(function (err) {
					console.log('getUserDevices enumerateDevices error', err, new Date().toISOString());
					let error = new IvcsError('70003', `getUserDevices enumerate deivces error ${err.name + ': ' + err.message}`);
					onError(error);
				});
		} catch (e) {
			console.log('getUserDevices crash error', e, new Date().toISOString());
			let error = new IvcsError('70004', `getUserDevices enumerate deivces error ${e}`);
			onError(error);
		}
	}

	/**
	 * 切换摄像头入口
	 * @param { Function } onSuccess 切换成功回调
	 * @param { Function } onError 切换失败回调
	 */
	switchCamera(onSuccess, onError) {
		if (this.userPhone === 'IOS') {
			if (this.facingMode === 'user') {
				this.facingMode = 'environment';
			} else {
				this.facingMode = 'user';
			}
			this.changeIosCamera(this.facingMode, onSuccess, onError);
		} else {
			this.getUserDevices(onSuccess, onError);
		}
	}

	/**
	 * 切换摄像头获取设备的deviceId。< ps:目前部分iPhone手机，遍历摄像头算法有问题，所以目前iPhone手机，返回deviceId为返回undefined，切换摄像头走的不是根据deviceId >
	 * @param {function} onSuccess {deviceId}
	 * @param {function} onFailure
	 * @returns
	 */
	switchCameraGetDeviceId(onSuccess, onFailure) {
		let userCameraDevices = [];
		if (!navigator.mediaDevices || !navigator.mediaDevices.enumerateDevices) {
			let error = new IvcsError('70001', 'devices not support');
			onFailure(error);
			return;
		}
		if (this.userPhone === 'IOS') {
			onSuccess(undefined);
			return;
		}
		try {
			navigator.mediaDevices
				.enumerateDevices()
				.then(devices => {
					devices.forEach(device => {
						if (device.kind === 'videoinput') {
							userCameraDevices.push({
								kind: device.kind,
								label: device.label,
								groupId: device.groupId,
								deviceId: device.deviceId,
							});
						}
					});
				})
				.then(() => {
					let device = this.filterAndroidCameraLabel(userCameraDevices);
					if (device.length < 0) {
						let error = new IvcsError('70002', 'notfound devices');
						onFailure(error);
					} else if (this.userPhone === 'Webview' || this.userPhone === 'Android') {
						this.deviceIndex === 0 ? (this.deviceIndex = device.length - 1) : (this.deviceIndex = 0);
					} else {
						this.deviceIndex = this.deviceIndex + 1;
						if (this.deviceIndex >= device.length) {
							this.deviceIndex = 0;
						}
					}
					let deviceId = device[this.deviceIndex].deviceId;
					onSuccess(deviceId);
				})
				.catch(err => {
					console.log('switchCameraGetDeviceId enumerateDevices error', err, new Date().toISOString());
					let error = new IvcsError('70003', `switchCameraGetDeviceId enumerate deivces error ${err.name + ': ' + err.message}`);
					onFailure(error);
				});
		} catch (e) {
			console.log('switchCameraGetDeviceId crash error', e, new Date().toISOString());
			let error = new IvcsError('70004', `switchCameraGetDeviceId enumerate deivces error ${e}`);
			onFailure(error);
		}
	}

	/**
	 * 根据设备类型去切换流，返回LocalStream,支持ivcs动态切换stream(publication.updateMedia),本流不会执行publish。只用做切换音视频源。
	 * @param {*} irtcClient
	 * @param {*} audioDeviceId
	 * @param {*} videoDeviceId
	 * @param {*} audioConstraints  {deviceId: undefined,source: 'mic'},
	 * @param {*} videoConstraints   {deviceId: undefined,frameRate: 15,resolution: {width: 640,height: 480},source: 'camera'},
	 * @param {*} onSuccess 成功返回stream
	 * @param {*} onFailure 失败
	 * @param {*} haveAudio 默认都是true，如果是false。则相应的audioConstraints 改成false
	 * @param {*} haveVideo 默认都是true，如果是false。则相应的videoConstraints 改成false
	 * @returns
	 */
	createStream(irtcClient, audioDeviceId, videoDeviceId, audioConstraints, videoConstraints, onSuccess, onFailure, haveAudio = true, haveVideo = true) {
		if (!irtcClient) {
			onFailure(new IvcsError('1000', 'irtcClient is null'));
			return;
		}
		if (isNullObject(audioConstraints)) {
			onFailure(new IvcsError('1000', 'audioConstraints is not object'));
			return;
		}
		if (isNullObject(videoConstraints)) {
			onFailure(new IvcsError('1000', 'videoConstraints is not object'));
			return;
		}
		const streamOptions = {
			audioConstraints: audioConstraints,
			videoConstraints: videoConstraints,
		};
		if (audioDeviceId) {
			audioConstraints.deviceId = audioDeviceId;
		}
		if (videoDeviceId) {
			videoConstraints.deviceId = videoDeviceId;
		}
		if (this.userPhone === 'IOS') {
			this.facingMode = this.facingMode === 'user' ? 'environment' : 'user';
			videoConstraints.facingMode = this.facingMode;
			videoConstraints.deviceId = undefined; // fix iphone13 promax multiple deviceId
		}
		Logger.info('ivcs-js-sdk:switch createStream options', streamOptions, new Date().toISOString());
		const videoLabel = '';
		const streamInfo = {};
		const userInfo = {};
		createCameraMicStream(irtcClient, streamOptions, videoLabel, haveAudio, haveVideo, streamInfo, userInfo)
			.then(stream => {
				onSuccess(stream);
			})
			.catch(error => {
				onFailure(error);
			});
	}

	/**
	 * 获取后置摄像头视频流，用于语音下，拿后置摄像头流去做业务操作
	 * @param {function} onSuccess mediaStream
	 * @param {function} onFailure {70000:'getUserMedia api error',70001:'navigator.mediaDevices.enumerateDevices api不支持',70002:无可用后置摄像头，70003:枚举设备报错，70008:切换成后置摄像头失败}
	 */
	getRearCameraVideoStream(onSuccess, onFailure) {
		if (this.userPhone === 'IOS') {
			let constraints = {
				audio: false,
				video: {
					facingMode: 'environment',
				},
			};
			this.getUserMedia(
				constraints,
				stream => {
					onSuccess(stream);
				},
				err => {
					let exceptionMsg = err.name || err.message || err;
					let errorCode = ivcsErrorCodeEnum.ERROR_ZMS_CREATESTREAM_FAILED;
					if (exceptionMsg.toLowerCase().indexOf('aborterror') != -1) {
						errorCode = ivcsErrorCodeEnum.ERROR_ZMS_ABORTERROR;
					} else if (exceptionMsg.toLowerCase().indexOf('notallowederror') != -1) {
						errorCode = ivcsErrorCodeEnum.ERROR_ZMS_NOTALLOWEDERROR;
					} else if (exceptionMsg.toLowerCase().indexOf('notfounderror') != -1) {
						errorCode = ivcsErrorCodeEnum.ERROR_ZMS_NOTFOUNDERROR;
					} else if (exceptionMsg.toLowerCase().indexOf('notreadableerror') != -1) {
						errorCode = ivcsErrorCodeEnum.ERROR_ZMS_NOTREADABLEERROR;
					} else if (exceptionMsg.toLowerCase().indexOf('overconstrainederror') != -1) {
						errorCode = ivcsErrorCodeEnum.ERROR_ZMS_OVERCONSTRAINEDERROR;
					} else if (exceptionMsg.toLowerCase().indexOf('securityerror') != -1) {
						errorCode = ivcsErrorCodeEnum.ERROR_ZMS_SECURITYERROR;
					} else if (exceptionMsg.toLowerCase().indexOf('typeerror') != -1) {
						errorCode = ivcsErrorCodeEnum.ERROR_ZMS_TYPEERROR;
					}
					let error = new IvcsError(errorCode, `ios getBackCameraVideoStream error ${err}`);
					onFailure(error);
				}
			);
		} else {
			// 安卓,pc
			const videoOnly = true;
			this.getUserDevices(onSuccess, onFailure, videoOnly);
		}
	}
}

export { Navigator };
